package data

import (
	"context"
	pb "digitalhuman/api/storyboard/v1"
	"digitalhuman/internal/biz"
	"digitalhuman/internal/helper"
	"digitalhuman/models"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/nats-io/nats.go"
	"github.com/wenqingqaq/backend-lib/tecmq"
	"gorm.io/gorm"
	"math/rand"
	"strconv"
	"time"
)

type StoryboardData struct {
	Data        *Data
	sectionData *StoryboardSectionData
	audioData   *TextAudioData
	x           *XunFei
	log         *log.Helper
	assetData   *AssetData
	userData    *UserData
	produceData *ProduceData
	VmData      *VisualMaterialD
}

func (u StoryboardData) Create(ctx context.Context, s *models.Storyboard) (*models.Storyboard, error) {
	u.Data.Db.Create(s)

	return s, nil
}

func (u StoryboardData) CreateStory(ctx context.Context, req *pb.CreateStoryboardRequestL, uid int) (*models.Storyboard, error) {
	// 创建Name按照时间戳拼接加上随机数字 生成
	rand.Seed(time.Now().UnixNano())
	randNum := rand.Intn(9999-1000) + 1000
	name := time.Now().Format("20060102150405") + "R" + strconv.Itoa(randNum)

	storyboard := &models.Storyboard{
		Name:        name,
		Description: "",
		Status:      int(pb.StoryboardStatusEnum_SECTION_DEFAULT.Number()),
		OwnerId:     uid,
		IsDraft:     int(req.IsDraft),
		CreatedAt:   time.Time{},
		UpdatedAt:   time.Time{},
	}
	storyboard, err := u.Create(context.Background(), storyboard)
	if err != nil {
		return nil, err
	}
	log.Info("storyboard创建成功 id = ", storyboard.ID)

	return storyboard, nil
}

func (u StoryboardData) Update(ctx context.Context, s *models.Storyboard) (*models.Storyboard, error) {
	u.Data.Db.Updates(s)

	return s, nil
}

func (u StoryboardData) UpdateState(ctx context.Context, sId int, state int) (bool, error) {
	u.Data.Db.Where("id = ?", sId).Update("state", state)

	return true, nil
}

func (u StoryboardData) FindByID(ctx context.Context, i int) (*models.Storyboard, error) {
	var s *models.Storyboard
	u.Data.Db.Where("id = ?", i).First(&s)

	return s, nil
}

func (u StoryboardData) FindByUserName(ctx context.Context, name string) (*models.Storyboard, error) {
	var user *models.Storyboard
	u.Data.Db.Where("name = ?", name).First(&user)
	if user == nil {
		return nil, errors.New("user not exist")
	}
	return user, nil
}

func (u StoryboardData) CreateStoryExtTable(ctx context.Context, uid int, storyboardId int, req *pb.CreateStoryboardRequestL) (*models.Storyboard, error) {
	// 根据传递的参数信息 创建附属表 有可能不存在
	storyboardReply, err := u.GetStoryWitExt(ctx, storyboardId)
	if err != nil {
		return nil, err
	}
	if req.IsDraft == 1 && storyboardReply.IsDraft == 0 {
		return nil, errors.New("已经提交过了 无需重复提交 storyboard_id = " + strconv.FormatInt(int64(storyboardId), 10))
	}

	// 参数验证一下 todo
	for _, sectionReqTe := range req.Requests {
		if sectionReqTe.BackgroundId == 0 && sectionReqTe.HumanId == 0 && sectionReqTe.TextContent == "" {
			return nil, errors.New("参数不全")
		}
		if sectionReqTe.HumanId != 0 && (sectionReqTe.Width == 0 || sectionReqTe.Height == 0) {
			return nil, errors.New("human 参数不全")
		}
	}

	// 开启事务
	tx := u.Data.Db.Begin()
	defer tx.Rollback()

	storyboard := &models.Storyboard{
		ID:      storyboardId,
		IsDraft: int(req.IsDraft),
	}
	storyboard, err = u.Update(ctx, storyboard)
	if err != nil {
		tx.Rollback()
		return nil, err
	}

	reqSectionOne := req.Requests[0]

	// 删除之前的记录 删除传递参数的附属表
	storySectionId := 0
	if len(storyboardReply.Sections) > 0 {
		for _, sectionReplyT := range storyboardReply.Sections {
			storySectionId = int(sectionReplyT.Id)
			//if sectionReplyT.Id != 0 {
			//	u.Data.Db.Where("id = ?", sectionReplyT.Id).Delete(&models.StoryboardSection{})
			//}
			if sectionReplyT.Human != nil && sectionReplyT.Human.Id != 0 && reqSectionOne.HumanId > 0 {
				u.Data.Db.Where("id = ?", sectionReplyT.Human.Id).Delete(&models.VisualMaterial{})
			}
			if sectionReplyT.Background != nil && sectionReplyT.Background.Id != 0 && reqSectionOne.BackgroundId > 0 {
				u.Data.Db.Where("id = ?", sectionReplyT.Background.Id).Delete(&models.VisualMaterial{})
			}
			if sectionReplyT.Text != nil && sectionReplyT.Text.Id != 0 && reqSectionOne.TextContent != "" {
				u.Data.Db.Where("id = ?", sectionReplyT.Text.Id).Delete(&models.TextAudio{})
			}
		}
	}

	// 遍历req 创建section新的记录以及相关的附属表
	for _, sectionReq := range req.Requests {
		// 创建 visual_material 记录
		humanVmModelId := 0
		if sectionReq.HumanId != 0 { // 传递了human_id就是要更新或者创建human信息的
			// 创建
			humanVmModel := models.VisualMaterial{
				StoryboardId: storyboardId,
				OwnId:        uid,
				AssetId:      int(sectionReq.HumanId),
				X:            int(sectionReq.X),
				Y:            int(sectionReq.Y),
				Width:        int(sectionReq.Width),
				Height:       int(sectionReq.Height),
			}
			u.Data.Db.Create(&humanVmModel)
			humanVmModelId = humanVmModel.Id
		}

		// 传递了背景信息的去创建
		backgroundVmModelId := 0
		if sectionReq.BackgroundId != 0 {
			humanVmModel := models.VisualMaterial{
				StoryboardId: storyboardId,
				OwnId:        uid,
				AssetId:      int(sectionReq.BackgroundId),
			}
			u.Data.Db.Create(&humanVmModel)
			backgroundVmModelId = humanVmModel.Id
		}

		// 传递了文本内容就去创建 TextAudio记录
		textAudioId := 0
		if sectionReq.TextContent != "" {
			textAudio := &models.TextAudio{
				StoryboardId: storyboardId,
				TextContent:  sectionReq.TextContent,
				TextVcn:      sectionReq.TextVcn,
				OwnerId:      uid,
			}
			_, err = u.audioData.Create(ctx, textAudio)
			if err != nil {
				tx.Rollback()
				return nil, err
			}
			textAudioId = textAudio.ID
		}

		// 保存场景信息，目前只有一个场景
		if storySectionId != 0 { // 这里是更新操作
			section := &models.StoryboardSection{}
			if humanVmModelId != 0 {
				section.HumanVmId = humanVmModelId
			}
			if backgroundVmModelId != 0 {
				section.BackgroundVmId = backgroundVmModelId
			}
			if textAudioId != 0 {
				section.TextAudioId = textAudioId
			}
			section.ID = storySectionId
			section, err := u.sectionData.Update(ctx, section)
			if err != nil {
				tx.Rollback()
				return nil, err
			}
		} else {
			section := &models.StoryboardSection{
				StoryboardId:   storyboardId,
				HumanVmId:      humanVmModelId,
				BackgroundVmId: backgroundVmModelId,
				OwnerId:        uid,
				TextAudioId:    textAudioId,
			}
			section, err := u.sectionData.Create(ctx, section)
			if err != nil {
				tx.Rollback()
				return nil, err
			}
			storySectionId = section.ID
			u.Data.Db.Model(&models.TextAudio{}).Where("id = ?", textAudioId).Update("storyboard_section_id", storySectionId)
		}
	}
	tx.Commit()
	log.Info("创建storyboard section textAudio 数据表内容，并返回了结果")

	return storyboard, nil
}

func (u StoryboardData) HandleSection(storyBoard *models.Storyboard) error {
	if storyBoard == nil {
		return errors.New("storyBoard id is null ")
	}
	log.Info("HandleSection start 创建语音合成任务 ----- storyboard_id = :", storyBoard.ID)
	if storyBoard.IsDraft == 1 {
		log.Info("草稿数据先不处理 storyboard_id = ", storyBoard.ID)
		return nil
	}
	textAudio := u.audioData.FindByStoryId(context.Background(), storyBoard.ID)
	if textAudio == nil {
		log.Error("textAudio is nil , 还没有传递文本内容")
		return errors.New("textAudio is nil")
	}
	if textAudio.TextAudioUrl != "" {
		log.Info("section has audio textAudio :", textAudio.ID)
		return nil
	}

	// 下面是去讯飞创建语音合成任务，然后轮训查询任务状态
	var dtsCreate *ResponseCreate
	var err error
	dtsCreate, err = u.x.ReqXunFeiDtsCreate(textAudio.TextContent, textAudio.TextVcn)
	if err != nil || dtsCreate.Header.TaskID == "" {
		log.Error("dtsCreateErr1 ", err)
		// 重试一次
		dtsCreate, err = u.x.ReqXunFeiDtsCreate(textAudio.TextContent, textAudio.TextVcn)
		if err != nil || dtsCreate.Header.TaskID == "" {
			log.Error("dtsCreateErr2 ", err)
			err := u.UpdateStatus(textAudio, int(pb.StoryboardStatusEnum_SECTION_STATUS_AUDIO_FAILURE.Number()))
			if err != nil {
				log.Error(err)
				return err
			}
			return err
		}
	}

	// 任务创建成功了，更新section的taskId 状态
	u.Data.Db.Where(&models.TextAudio{ID: textAudio.ID}).Updates(&models.TextAudio{
		TextAudioTaskId: dtsCreate.Header.TaskID,
		Status:          int(pb.AudioStatusEnum_AUDIO_STATUS_IN_PRODUCTION.Number()),
	})
	textAudio.TextAudioTaskId = dtsCreate.Header.TaskID

	// 异步轮训，查询一下任务状态，最大尝试5次
	fmt.Println(dtsCreate)
	go u.CheckAudioResult(textAudio)

	return nil
}

func (u StoryboardData) CheckAudioResult(textAudioM *models.TextAudio) {
	// 尝试5次查询任务状态，如果完成了，就更新section的音频链接
	for i := 0; i < 5; i++ {
		time.Sleep(5 * time.Second)
		resp := u.x.ReqXunFeiDtsQuery(textAudioM.TextAudioTaskId)
		fmt.Println(resp)
		if resp.Header.TaskStatus == TaskStatusCompleted {
			log.Info("语音合成任务成功 audio_id = ", textAudioM.ID)
			err := u.UpdateStatus(textAudioM, int(pb.StoryboardStatusEnum_SECTION_STATUS_AUDIO_COMPLETE.Number()))
			if err != nil {
				log.Error(err)
			}
			if resp.Payload.Audio.Audio == "" {
				log.Error("audio url is empty audio_id = ", textAudioM.ID)
				break
			}
			// 获取合成语音时长
			second, err := helper.GetMp3UrlDuration(resp.Payload.Audio.Audio)
			if err != nil || second <= 0 {
				log.Error("获取文件时长失败请注意 audio_id = ", textAudioM.ID, err)
				break
			}

			resultE := u.Data.Db.Model(&models.TextAudio{}).Where(&models.TextAudio{ID: textAudioM.ID}).Updates(&models.TextAudio{
				TextAudioUrl: resp.Payload.Audio.Audio,
				Duration:     second,
				Status:       int(pb.StoryboardStatusEnum_SECTION_STATUS_AUDIO_COMPLETE.Number()),
			})
			if resultE.Error != nil {
				log.Error("更新数据表audioData失败 ", err)
				break
			}

			log.Info(fmt.Sprintf("更新用户duration计费数据 audio_id = %d user_id = %d", textAudioM.ID, textAudioM.OwnerId))
			u.UpdateDuration(textAudioM, second)

			// 这里是发送生产视频的请求到produce服务
			go func(storyId int) {
				reply, err := u.GetStoryWitExt(context.Background(), textAudioM.StoryboardId)
				if err != nil {
					log.Error(err)
					return
				}
				if u.Data.Config.Nats.UseNats {
					err = u.produceData.ReqProduceToNats(reply)
					if err != nil {
						log.Error("生产视频发送nats失败 ", err)
					}
				} else {
					u.produceData.ReqProduceCreate(reply) // 这个是直接通过http发送的
				}
			}(textAudioM.StoryboardId)

			return
		}
	}

	// 如果5次都没有更新成功，就标记一下storyboard state为失败状态
	log.Error("轮训查询任务状态失败 请手动查询 audio_id = ", textAudioM.ID)
	err := u.UpdateStatus(textAudioM, int(pb.StoryboardStatusEnum_SECTION_STATUS_AUDIO_FAILURE.Number()))
	if err != nil {
		log.Error(err)
	}
}

func (u StoryboardData) VideoMake(sectionId int) {
	// 拼装一下视频合成所需要的参数 音频地址 背景地址 人物地址
	/**
	args.audio = audio_path
	args.face = f'data/human/{human_assets_payload}'
	args.outfile = f'data/out/{storyboard_id}.mp4'
	args.background = f'data/background/{background_assets_payload}'
	args.is_background_video = background_assets_type == 4  # 3-图片 # 4-视频
	args.face_x = human_x
	args.face_y = human_y
	args.face_width = human_width
	args.face_height = human_height
	*/

}

// UpdateDuration 更新记录的时长 事务更新 user 和 text_audio 两个表的
func (u StoryboardData) UpdateDuration(textAudioM *models.TextAudio, second int) {
	// 使用事务同时更新user和text_audio表
	tx := u.Data.Db.Begin()
	defer tx.Rollback()

	b, oldDuration, err := u.userData.UpdateUserDuration(textAudioM.OwnerId, second)
	if err != nil {
		tx.Rollback()
		log.Error(err)
		return
	}
	if b {
		result := u.Data.Db.Model(&models.TextAudio{}).Where(&models.TextAudio{ID: textAudioM.ID}).Updates(&models.TextAudio{
			Duration:      second,
			DurationTotal: oldDuration,
		})
		if result.Error != nil {
			tx.Rollback()
			log.Error(err)
			return
		}
		tx.Commit()
	} else {
		tx.Rollback()
	}
}

// UpdateStatus 更新记录的状态 事务更新 storyboard section textAudio 三个表的
func (u StoryboardData) UpdateStatus(textAudioM *models.TextAudio, status int) error {
	err := u.Data.Db.Transaction(func(tx *gorm.DB) error {
		result := u.Data.Db.Model(&models.Storyboard{}).Where("id = ?", textAudioM.StoryboardId).Update("status", status)
		if result.Error != nil {
			return result.Error
		}
		result = u.Data.Db.Model(&models.StoryboardSection{}).Where("id = ?", textAudioM.StoryboardSectionId).Update("status", status)
		if result.Error != nil {
			return result.Error
		}
		result = u.Data.Db.Model(&models.TextAudio{}).Where("id = ?", textAudioM.ID).Update("status", status)
		if result.Error != nil {
			return result.Error
		}
		// 返回 nil 提交事务
		return nil
	})
	if err != nil {
		log.Error(err)
		return err
	}
	return nil
}

func (u StoryboardData) DeleteStoryById(storyId int, uid int) error {
	// 先查询一下记录
	var storyboard *models.Storyboard
	u.Data.Db.Where(&models.Storyboard{ID: storyId, OwnerId: uid}).First(&storyboard)
	if storyboard.OwnerId != uid {
		return errors.New("没有权限删除")
	}
	if storyboard.Status >= int(pb.StoryboardStatusEnum_SECTION_STATUS_VIDEO_COMPLETE.Number()) {
		return errors.New("视频合成任务已经完成了, 无需处理了 storyboard_id =" + strconv.FormatInt(int64(storyId), 10))
	}

	reply, err := u.GetStoryWitExt(context.Background(), storyId)
	if err != nil {
		return err
	}

	// 查询storyboard_section
	for _, sectionReplyT := range reply.Sections {
		u.Data.Db.Where("id = ?", sectionReplyT.Id).Delete(&models.StoryboardSection{})
		if sectionReplyT.Human != nil && sectionReplyT.Human.Id != 0 {
			u.Data.Db.Where("id = ?", sectionReplyT.Human.Id).Delete(&models.VisualMaterial{})
		}
		if sectionReplyT.Background != nil && sectionReplyT.Background.Id != 0 {
			u.Data.Db.Where("id = ?", sectionReplyT.Background.Id).Delete(&models.VisualMaterial{})
		}
		if sectionReplyT.Text != nil && sectionReplyT.Text.Id != 0 {
			u.Data.Db.Where("id = ?", sectionReplyT.Text.Id).Delete(&models.TextAudio{})
		}
	}

	return nil
}

func (u StoryboardData) GetStoriesWitExtByUid(ctx context.Context, ownId int, isDraft int) (*pb.ListStoryboardReply, error) {
	storyboardSectionGetDatas := &pb.ListStoryboardReply{}
	var storyboards []*models.Storyboard
	// 通过用户id查询storyboard表左连接查询一下 storyboard_section表
	u.Data.Db.Where(&models.Storyboard{OwnerId: ownId, IsDraft: isDraft}).Limit(100).Order("id").Find(&storyboards)

	var sections []*models.StoryboardSection
	u.Data.Db.Where(&models.StoryboardSection{OwnerId: ownId}).Limit(200).Find(&sections)
	storySectionMap := make(map[int][]*models.StoryboardSection)
	for _, section := range sections {
		storySectionMap[section.StoryboardId] = append(storySectionMap[section.StoryboardId], section)
	}

	var textAudios []*models.TextAudio
	u.Data.Db.Where(&models.TextAudio{OwnerId: ownId}).Limit(200).Find(&textAudios)
	textAudionMap := make(map[int]*models.TextAudio)
	for _, textAudio := range textAudios {
		textAudionMap[textAudio.ID] = textAudio
	}

	var visualMaterials []*models.VisualMaterial
	u.Data.Db.Where(&models.VisualMaterial{OwnId: ownId}).Limit(200).Find(&visualMaterials)
	// 创建一个map 通过section_id查询visual_material表
	VmMap := make(map[int]*models.VisualMaterial)
	for _, visualMaterial := range visualMaterials {
		VmMap[visualMaterial.Id] = visualMaterial
	}

	preUrl := "https://" + u.Data.Config.OssAli.Bucket + "." + u.Data.Config.OssAli.Endpoint
	for _, storyboard := range storyboards {
		storyboardSectionGetData := &pb.GetStoryboardReply{}
		storyboardSectionGetData.Id = int32(storyboard.ID)
		storyboardSectionGetData.Name = storyboard.Name
		storyboardSectionGetData.Description = storyboard.Description
		storyboardSectionGetData.IsDraft = int32(storyboard.IsDraft)

		// 获取storyboard下面的sections，通过map
		if _, ex := storySectionMap[storyboard.ID]; !ex {
			continue
		}

		for _, section := range storySectionMap[storyboard.ID] {
			pbSection := &pb.Section{}
			pbSection.Id = int32(section.ID)
			pbSection.VideoUrl = section.VideoUrl
			if section.VideoUrl != "" {
				pbSection.VideoUrl = preUrl + section.VideoUrl
			}
			pbSection.VideoDuration = int32(section.VideoDuration)
			pbSection.VideoCreatedAt = section.VideoCreateTime.Format("2006-01-02 15:04:05")
			pbSection.VideoExpiredAt = section.VideoExpiredTime.Format("2006-01-02 15:04:05")
			_, exVm := VmMap[section.HumanVmId]
			if section.HumanVmId != 0 && exVm {
				humanAsset := u.assetData.FindAssetFromAssetMap(VmMap[section.HumanVmId].AssetId)
				pbSection.Human = &pb.VisualAsset{}
				pbSection.Human.Id = int32(section.HumanVmId)
				pbSection.Human.X = int32(VmMap[section.HumanVmId].X)
				pbSection.Human.Y = int32(VmMap[section.HumanVmId].Y)
				pbSection.Human.Width = int32(VmMap[section.HumanVmId].Width)
				pbSection.Human.Height = int32(VmMap[section.HumanVmId].Height)
				pbSection.Human.Url = humanAsset.Thumbnail
				// asset资源也返回一下
				pbSection.Human.Asset = &pb.Asset{
					Id:        int32(humanAsset.ID),
					Thumbnail: humanAsset.Thumbnail,
					Payload:   humanAsset.Payload,
					Type:      int32(humanAsset.Type),
					Name:      humanAsset.Name,
				}
			}
			_, ex := VmMap[section.BackgroundVmId]
			if section.BackgroundVmId != 0 && ex {
				backAsset := u.assetData.FindAssetFromAssetMap(VmMap[section.BackgroundVmId].AssetId)
				pbSection.Background = &pb.VisualAsset{}
				pbSection.Background.Id = int32(section.BackgroundVmId)
				pbSection.Background.Url = backAsset.Thumbnail
				pbSection.Background.Asset = &pb.Asset{
					Id:        int32(backAsset.ID),
					Thumbnail: backAsset.Thumbnail,
					Payload:   backAsset.Payload,
					Type:      int32(backAsset.Type),
					Name:      backAsset.Name,
				}
			}
			_, exT := textAudionMap[section.TextAudioId]
			if section.TextAudioId != 0 && exT {
				textAudio := textAudionMap[section.TextAudioId]
				pbSection.Text = &pb.TextAudio{}
				pbSection.Text.Url = textAudio.TextAudioUrl
				pbSection.Text.Vcn = textAudio.TextVcn
				pbSection.Text.Content = textAudio.TextContent
				pbSection.Text.Id = int32(textAudio.ID)
			}

			storyboardSectionGetData.Sections = append(storyboardSectionGetData.Sections, pbSection)
		}

		storyboardSectionGetDatas.Storyboards = append(storyboardSectionGetDatas.Storyboards, storyboardSectionGetData)
	}

	return storyboardSectionGetDatas, nil
}

func (u StoryboardData) GetStoryWitExt(ctx context.Context, storyId int) (*pb.GetStoryboardReply, error) {
	storyboardSectionGetData := &pb.GetStoryboardReply{}
	// 查询storyboard表left join storyboard_section表
	var storyboard *models.Storyboard
	storyResult := u.Data.Db.Where("id = ?", storyId).First(&storyboard)
	if storyResult.Error != nil {
		return &pb.GetStoryboardReply{}, storyResult.Error
	}

	storyboardSectionGetData.Id = int32(storyboard.ID)
	storyboardSectionGetData.Name = storyboard.Name
	storyboardSectionGetData.Description = storyboard.Description
	storyboardSectionGetData.IsDraft = int32(storyboard.IsDraft)

	// 查询storyboard_section表
	var sections []*models.StoryboardSection
	sectionResult := u.Data.Db.Where("storyboard_id = ?", storyId).Find(&sections)
	if sectionResult.Error != nil {
		return nil, sectionResult.Error
	}

	preUrl := "https://" + u.Data.Config.OssAli.Bucket + "." + u.Data.Config.OssAli.Endpoint
	for _, section := range sections {
		var textAudio *models.TextAudio
		u.Data.Db.Where("storyboard_section_id = ?", section.ID).First(&textAudio)

		pbSection := &pb.Section{}
		pbSection.Id = int32(section.ID)
		if section.VideoUrl != "" {
			pbSection.VideoUrl = preUrl + section.VideoUrl
		} else {
			pbSection.VideoUrl = ""
		}
		pbSection.VideoDuration = int32(section.VideoDuration)
		pbSection.VideoCreatedAt = section.VideoCreateTime.Format("2006-01-02 15:04:05")
		pbSection.VideoExpiredAt = section.VideoExpiredTime.Format("2006-01-02 15:04:05")

		mapVm := u.VmAssetFromSectionId([]int{section.HumanVmId, section.BackgroundVmId})
		if section.HumanVmId != 0 {
			pbSection.Human = &pb.VisualAsset{}
			pbSection.Human.Id = int32(section.HumanVmId)
			pbSection.Human.X = int32(mapVm[section.HumanVmId].X)
			pbSection.Human.Y = int32(mapVm[section.HumanVmId].Y)
			pbSection.Human.Width = int32(mapVm[section.HumanVmId].Width)
			pbSection.Human.Height = int32(mapVm[section.HumanVmId].Height)
			if _, ex := mapVm[section.HumanVmId]; ex {
				pbSection.Human.Url = mapVm[section.HumanVmId].AssetUrl
				humanAsset := u.assetData.FindAssetFromAssetMap(mapVm[section.HumanVmId].AssetId)
				pbSection.Human.Asset = &pb.Asset{
					Id:        int32(humanAsset.ID),
					Thumbnail: humanAsset.Thumbnail,
					Payload:   humanAsset.Payload,
					Type:      int32(humanAsset.Type),
					Name:      humanAsset.Name,
				}
			} else {
				pbSection.Human.Url = ""
			}
		}

		if section.BackgroundVmId != 0 {
			pbSection.Background = &pb.VisualAsset{}
			pbSection.Background.Id = int32(section.BackgroundVmId)
			if _, ex := mapVm[section.BackgroundVmId]; ex {
				pbSection.Background.Url = mapVm[section.BackgroundVmId].AssetUrl
				backAsset := u.assetData.FindAssetFromAssetMap(mapVm[section.BackgroundVmId].AssetId)
				pbSection.Background.Asset = &pb.Asset{
					Id:        int32(backAsset.ID),
					Thumbnail: backAsset.Thumbnail,
					Payload:   backAsset.Payload,
					Type:      int32(backAsset.Type),
					Name:      backAsset.Name,
				}
			} else {
				pbSection.Background.Url = ""
			}
		}

		if textAudio.ID != 0 {
			pbSection.Text = &pb.TextAudio{}
			pbSection.Text.Url = textAudio.TextAudioUrl
			pbSection.Text.Vcn = textAudio.TextVcn
			pbSection.Text.Content = textAudio.TextContent
			pbSection.Text.Id = int32(textAudio.ID)
		}

		storyboardSectionGetData.Sections = append(storyboardSectionGetData.Sections, pbSection)
	}

	return storyboardSectionGetData, nil
}

func (u StoryboardData) GetStoryListByUid(ctx context.Context, uid int, isDraft int) (*pb.ListStoryboardReply, error) {
	storyboardSectionGetData := &pb.ListStoryboardReply{}
	var storyboards []*models.Storyboard
	// 通过用户id查询storyboard表
	storyResult := u.Data.Db.Where("owner_id = ? and is_draft = ?", uid, isDraft).Limit(50).Find(&storyboards)
	if storyResult.Error != nil {
		return &pb.ListStoryboardReply{}, storyResult.Error
	}
	for _, storyboard := range storyboards {
		res, err := u.GetStoryWitExt(ctx, storyboard.ID)
		if err != nil {
			return nil, err
		}
		storyboardSectionGetData.Storyboards = append(storyboardSectionGetData.Storyboards, res)
	}

	return storyboardSectionGetData, nil
}

func (u StoryboardData) VmAssetFromSectionId(sectionIdArr []int) map[int]*VisualMaterialData {
	visualMaterials := make(map[int]*VisualMaterialData)
	// 查询 visual_material 表，通过 sql in 查询，获取 section.HumanVmId  section.BackgroundVmId 对应的数据
	var vms []*VisualMaterialData
	result := u.Data.Db.Table("visual_materials").Where("id IN (?)", sectionIdArr).Find(&vms)
	if result.Error != nil {
		return nil
	}
	for _, vm := range vms {
		asset := u.assetData.FindAssetFromAssetMap(vm.AssetId)
		vm.AssetUrl = asset.Thumbnail
		visualMaterials[int(vm.Id)] = vm
	}

	return visualMaterials
}

func (u StoryboardData) CallBackProduce(ctx context.Context, req *pb.CallBackProduceRequest) error {
	// ToDo 后期有时间的时候 再写一个轮训脚本 去保障视频合成任务的完成和标记彻底失败的视频

	// 查询一下数据表的storyboard内容
	var storyboard *models.Storyboard
	u.Data.Db.Where("id = ?", req.StoryboardId).First(&storyboard)
	if storyboard.Status >= int(pb.StoryboardStatusEnum_SECTION_STATUS_VIDEO_FAILURE.Number()) {
		log.Info("视频合成任务已经完成或者失败了, 无需处理了 storyboard_id = ", req.StoryboardId)
		return nil
	}

	if req.IsSuccess == 0 {
		u.Data.Db.Model(&models.Storyboard{}).Where("id = ?", req.StoryboardId).Updates(&models.Storyboard{
			Status: int(pb.StoryboardStatusEnum_SECTION_STATUS_VIDEO_FAILURE.Number()),
		})
		u.Data.Db.Model(&models.StoryboardSection{}).Where("id = ?", req.StoryboardSectionId).Updates(&models.StoryboardSection{
			Status: int(pb.StoryboardStatusEnum_SECTION_STATUS_VIDEO_FAILURE.Number()),
		})
	} else {
		u.Data.Db.Model(&models.Storyboard{}).Where("id = ?", req.StoryboardId).Updates(&models.Storyboard{
			Status: int(pb.StoryboardStatusEnum_SECTION_STATUS_VIDEO_COMPLETE.Number()),
		})

		f, err := strconv.ParseFloat(req.Duration, 64)
		if err != nil {
			log.Error(err)
		}

		// 将浮点数转换为整数
		i := f * 1000

		u.Data.Db.Model(&models.StoryboardSection{}).Where("id = ?", req.StoryboardSectionId).Updates(&models.StoryboardSection{
			Status:           int(pb.StoryboardStatusEnum_SECTION_STATUS_VIDEO_COMPLETE.Number()),
			VideoUrl:         req.OutFile,
			VideoCreateTime:  time.Now(),
			VideoDuration:    int(i), // 22.111 -> 23
			VideoExpiredTime: time.Now().AddDate(0, 1, 0),
		})
	}

	return nil
}

type VisualMaterialData struct {
	models.VisualMaterial
	AssetUrl string
}

func (u StoryboardData) StartNatsSubCallBack() error {
	if u.Data.Config.Nats.UseNats { // nats接收回调请求消息 先不需要 监听的http回调
		fmt.Println("nas监听中")
		_, err := u.Data.Nats.client.JsCtx.QueueSubscribe(u.Data.Config.Nats.CallbackSubject, u.Data.Config.Nats.Queue, tecmq.HandlerFucAndAck(func(msgC *tecmq.MqMsg) {
			fmt.Println("监听到nats数据  ", string(msgC.Msg.Data))
			var produceResultMsg *tecmq.ProduceResultMsg
			err := json.Unmarshal(msgC.Msg.Data, &produceResultMsg)
			if err != nil {
				log.Info(err)
				return
			}
			// 处理请求
			err = u.CallBackProduce(context.Background(), &pb.CallBackProduceRequest{
				StoryboardId:        produceResultMsg.Data.StoryboardId,
				IsSuccess:           produceResultMsg.Data.IsSuccess,
				Duration:            produceResultMsg.Data.Duration,
				StoryboardSectionId: produceResultMsg.Data.StoryboardSectionId,
				OutFile:             produceResultMsg.Data.OutFile,
			})
			if err != nil {
				log.Info(err)
				return
			}
			err = msgC.Msg.Ack()
			if err != nil {
				log.Info(err)
				return
			}
			fmt.Println("nats 执行结束 story_id = ", produceResultMsg.Data.StoryboardId)
		}), nats.Durable("produce-consumer"), nats.ManualAck(), nats.MaxDeliver(3))
		if err != nil {
			panic(err)
		}
	}

	return nil
}

func NewStoryboardData(data *Data, sectionData *StoryboardSectionData, audioData *TextAudioData, x *XunFei, logger log.Logger,
	assetData *AssetData, produceData *ProduceData, vm *VisualMaterialD,
	userData *UserData) biz.StoryRepo {

	storyData := StoryboardData{
		Data:        data,
		sectionData: sectionData,
		audioData:   audioData,
		x:           x,
		log:         log.NewHelper(logger),
		assetData:   assetData,
		userData:    userData,
		produceData: produceData,
		VmData:      vm,
	}

	return storyData
}

type StoryboardSectionGetData struct {
	models.Storyboard
	Sections []*models.StoryboardSection
}

var _ biz.StoryRepo = (*StoryboardData)(nil)
